use sqlx::PgPool;

use crate::{db, filter, model, utils, Error, Result};

pub async fn add(p: &PgPool, m: &model::user::User) -> Result<String> {
    let mut tx = p.begin().await.map_err(Error::from)?;

    let exists = match db::user::is_exists(&mut *tx, &m.email, None).await {
        Ok(is_exists) => is_exists,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };

    if exists {
        tx.rollback().await.map_err(Error::from)?;
        return Err(Error::already_exists("邮箱已存在"));
    }

    let id = match db::user::create(&mut *tx, m).await {
        Ok(id) => id,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };
    tx.commit().await.map_err(Error::from)?;

    Ok(id)
}

pub async fn find<'a>(p: &PgPool, f: &filter::user::Find<'a>) -> Result<Option<model::user::User>> {
    db::user::find(p, f).await.map_err(Error::from)
}

pub async fn list<'a>(
    p: &PgPool,
    f: &filter::user::List<'a>,
) -> Result<db::Pagination<model::user::User>> {
    let mut tx = p.begin().await.map_err(Error::from)?;

    let total = match db::user::list_count(&mut *tx, f).await {
        Ok(v) => v,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };

    let data = match db::user::list_data(&mut *tx, f).await {
        Ok(v) => v,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };

    tx.commit().await.map_err(Error::from)?;

    Ok(db::Pagination::quick(total, &f.pagination, data))
}

pub async fn edit(p: &PgPool, m: &model::user::User, skip_update_pwd: bool) -> Result<u64> {
    if m.id.is_empty() {
        return Err(Error::invalid_parameter("未指定ID"));
    }
    let mut tx = p.begin().await.map_err(Error::from)?;

    let exists = match db::user::is_exists(&mut *tx, &m.email, Some(&m.id)).await {
        Ok(is_exists) => is_exists,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };

    if exists {
        tx.rollback().await.map_err(Error::from)?;
        return Err(Error::already_exists("邮箱已存在"));
    }

    let aff = match db::user::update(p, m, skip_update_pwd).await {
        Ok(v) => v,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };
    tx.commit().await.map_err(Error::from)?;

    Ok(aff)
}

pub async fn change_password(
    p: &PgPool,
    id: &str,
    hashed_new_password: &str,
    password: Option<&str>,
) -> Result<u64> {
    let mut tx = p.begin().await.map_err(Error::from)?;
    if let Some(password) = password {
        let user = match db::user::find(
            &mut *tx,
            &filter::user::Find {
                by: filter::user::FindBy::ID(id),
                status: None,
                role: None,
            },
        )
        .await
        {
            Ok(v) => match v {
                Some(v) => v,
                None => {
                    tx.rollback().await.map_err(Error::from)?;
                    return Err(Error::not_found("不存在的用户"));
                }
            },
            Err(e) => {
                tx.rollback().await.map_err(Error::from)?;
                return Err(Error::from(e));
            }
        };

        if !utils::password::verify(password, &user.password)? {
            return Err(Error::invalid_parameter("现用密码错误"));
        }
    }

    let aff = match db::user::update_password(&mut *tx, id, hashed_new_password).await {
        Ok(v) => v,
        Err(e) => {
            tx.rollback().await.map_err(Error::from)?;
            return Err(Error::from(e));
        }
    };

    tx.commit().await.map_err(Error::from)?;

    Ok(aff)
}

pub async fn change_status(p: &PgPool, id: &str, status: &model::user::Status) -> Result<u64> {
    db::user::update_status(p, id, status)
        .await
        .map_err(Error::from)
}

pub async fn del(p: &PgPool, id: &str) -> Result<u64> {
    db::user::del(p, id).await.map_err(Error::from)
}

pub async fn user_login(p: &PgPool, email: &str, password: &str) -> Result<model::user::User> {
    _login(p, password, &filter::user::Find::user(email), false).await
}
pub async fn admin_login(p: &PgPool, email: &str, password: &str) -> Result<model::user::User> {
    _login(p, password, &filter::user::Find::admin(email), true).await
}

async fn _login<'a>(
    p: &PgPool,
    password: &str,
    f: &filter::user::Find<'a>,
    skip_check_status: bool,
) -> Result<model::user::User> {
    let m = db::user::find(p, f).await.map_err(Error::from)?;

    let m = match m {
        Some(v) => v,
        None => return Err(Error::not_found("邮箱或密码错误1")),
    };

    if !skip_check_status {
        // 状态
        match &m.status {
            &model::user::Status::Actived => {
                // pass
            }
            &model::user::Status::Freezed => return Err(Error::forbidden("账号已冻结")),
            &model::user::Status::Pending => return Err(Error::forbidden("账号未激活")),
        };
    }

    // 校验密码
    if !utils::password::verify(password, &m.password)? {
        return Err(Error::forbidden("邮箱或密码错误2"));
    }

    Ok(m)
}
